/*
 * @Author: CB
 * @Date: 2023-11-15 17:17:48
 * @Description: 对象池工厂
 */
import { instantiate } from 'cc';
import { _decorator, Node, } from 'cc';
import { Prefab } from 'cc';
import { Logger } from '../../core/common/log/Logger';
import { LinkList } from 'db://oops-framework/libs/collection/LinkList';
const { ccclass, property } = _decorator;


@ccclass('NodePoolFactory')
export class NodePoolFactory {
    private waitNodes: LinkList<Node> = new LinkList();
    private usingNodes: Map<string, Node> = new Map<string, Node>();

    template: Node | Prefab = null;

    max: number = 0;

    beforeAlloc?: (node: Node) => void = null;
    beforeFree?: (node: Node) => void = null;

    constructor(prefab: Node | Prefab, max: number) {
        this.template = prefab;
        this.max = max;
    }

    alloc(): Node {
        let result: Node = null;
        if (this.waitNodes.size > 0) {
            result = this.waitNodes.shift();
            result.active = true;
            if (!result.isValid) {
                Logger.trace('释放时机可能有问题，释放和创建不要在同一帧，不要在 onDestory 去回收');
            }
        }
        if (!result || !result.isValid) {
            result = instantiate(this.template) as Node;
        }

        let item = this.usingNodes.get(result.uuid);
        item && item.isValid && item.destroy();
        this.usingNodes.set(result.uuid, result);

        result.active = true;
        this.beforeAlloc && this.beforeAlloc(result);
        return result;
    }

    destroy() {
        Logger.trace('清空内存池');
        this.template = null;

        if (this.waitNodes) {
            this.waitNodes.forEach(item => {
                item?.destroy();
            })
            this.waitNodes.clear();
            this.waitNodes = null;
        }
        if (this.usingNodes) {
            this.usingNodes.forEach((item, k) => {
                item?.destroy();
            });
            this.usingNodes.clear();
            this.usingNodes = null;
        }
    }

    /** 回收所有节点 */
    autoFree() {
        let items = this.usingNodes.values();
        for (let item of items) {
            this.free(item);
        }
    }

    free(node: Node) {
        if (!node) return;
        if (!this.usingNodes.has(node.uuid)) {
            Logger.trace('节点已经释放过了' + node.name,);
            return;
        }
        this.usingNodes.delete(node.uuid);
        if (this.waitNodes.size >= this.max) {
            Logger.trace('超过内存池上限, 直接删除 max: ' + this.max);
            this.beforeFree && this.beforeFree(node);
            node?.destroy();
            return;
        }
        node.setParent(null);
        node.active = false;
        this.beforeFree && this.beforeFree(node);
        this.waitNodes.push(node);
    }

    /** 预加载 */
    preLoad(count: number, oneFrameCount: number): Promise<void> {
        if (count <= 0 || oneFrameCount <= 0) {
            return Promise.reject(new Error('Invalid parameters'));
        }
        count = Math.min(count, this.max); // 确保不超过最大限制
        const loadNodes = async () => {
            while (this.waitNodes.size < count) {
                this.createWaitNode(oneFrameCount);
                await new Promise(resolve => setTimeout(resolve, 1));
            }
        };
        return loadNodes();
    }



    /** 创建等待的节点 用于预加载 */
    private createWaitNode(count: number) {
        const gapCount = this.max - this.waitNodes.size;
        const createNum = Math.min(gapCount, count);
        for (let i = 0; i < createNum; i++) {
            let node = instantiate(this.template) as Node;
            node.active = false;
            this.waitNodes.push(node);
        }
    }
}
